package Buscaminas.Principal;

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.io.IOException;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

import Buscaminas.Dialogos.DialogoCambiarApariencia;
import Buscaminas.Dialogos.DialogoChivato;
import Buscaminas.Dialogos.DialogoEstadisticas;
import Buscaminas.Dialogos.DialogoGanar;
import Buscaminas.Puntuaciones.Estadisticas;
import Buscaminas.Puntuaciones.ExcepcionPuntuaciones;
import Buscaminas.Puntuaciones.Puntuacion;

public class Controlador implements ActionListener, MouseListener, Runnable{//Runnable para el cronometro
	
	/** 
	 * VARIABLES DE INSTANCIA
	 */
	// Objetos principales
	private Vista miVista;
	private Modelo miModelo;
	
	// DIALOGOS Y VARIABLES EN LAS QUE GUARDAREMOS DATOS DE ESOS DIALOGOS
	
	//------------------------------Se lanzan Manualmente----------------------------------//
	private DialogoCambiarApariencia ca;
	
	private int alto, ancho, minas;
	
	private Icon iBomba, iTablero, iBandera;// iconos que estamos usando actualmente (recibidos de ca)

	//------------------------------Se lanza Automaticamente-----------------------------------//
	private DialogoGanar g;// Cada vez que se pulse una casilla (ActionListener) se comprueba se se ha ganado con el m�todo heGanado() del Modelo, si se gana, Aparece el DialogoGanar
	
	// COLECCION DE PUNTUACIONES, ESTADISTICAS
	private Estadisticas est;
	
	// Otros
	private Font fCasillas;// fuente de los n� de las casillas
	private int numCasilla;// numero del interior de la casilla (reutilizada para cada casilla)
	
	// Variables para el funcionamiento del Cronometro
	private long segundos;// contador de segundos
	private Thread crono;// cronometro (nuevo hilo)
	private boolean cronoActivo;// indica si el cronometro esta activo o no
	
	/**
	 * CONSTRUCTOR
	 * @throws ExcepcionPuntuaciones 
	 */
	public Controlador(Vista v, Modelo m) throws ExcepcionPuntuaciones{
		
		/*
		 * Inicializar las variables de instancia
		 */
		this.miVista = v;// ya viene creada a 9x9 en el Main
		this.miModelo = m;// ya viene creado a 9x9 10bombas en el Main
		
		this.iBomba = new ImageIcon("Buscaminas/img/bomba1_16.png");//Icono Bomba Inicial
		this.iTablero = miVista.getiTableroDefecto();//inicializa el icono con el de por defecto de miVista
		this.iBandera = new ImageIcon("Buscaminas/img/tablero1_18_bandera.png");
		
		this.alto = miModelo.getAlto();//extrae el alto de miVista
		this.ancho = miModelo.getAncho();//extrae el ancho de miVista
		this.minas = miModelo.getMinas();//por defecto
		
		/*
		 *  Inicializar Estadisticas con el fichero de estadisticas correspondiente
		 */
		if(miModelo.getAlto()==9 && miModelo.getAncho()==9 && miModelo.getMinas()==10){//Principiante (9x9 10m)
			est = new Estadisticas('p');
		}
		else{
			if(miModelo.getAlto()==16 && miModelo.getAncho()==16 && miModelo.getMinas()==40){//Intermedio (16x16 40m)
				est = new Estadisticas('i');
System.out.println("Inter");
			}
			else{
				if(miModelo.getAlto()==16 && miModelo.getAncho()==30 && miModelo.getMinas()==99){//Avanzado (16x30 99m)
					est = new Estadisticas('a');
System.out.println("Avanzado");
				}
				else{//Personalizado (no almacenaremos las puntuaciones de partidas personalizadas)
					est = null;
				}
			}
		}
		
		//this.fCasillas = new Font("Comic Sans MS", Font.BOLD, 16);// Inicializar Fuente
		this.fCasillas = new Font("Segoe Script", Font.PLAIN, 16);
		
		//Cronometro
		segundos = 0;
		crono = new Thread(this);
		cronoActivo = false;
		
		/**
		 * Otros
		 */
		//Muestra en el Label de abajo a la derechoa el n� de Bombas
		this.miVista.getlBombas().setText(String.valueOf(miModelo.getMinas()));
	}
	
	/**
	 * M�todo de la interfaz ActionListener
	 */
	public void actionPerformed(ActionEvent e) {

		Object ob = e.getSource();
		
		// Casillas del buscaminas
		for(int i=0; i<alto; i++)
			for(int k=0; k<ancho; k++)
				//pregunta por la casilla pulsada, cada vez que se pulsa una casilla se comprueba si ha: perdido, ganado y si se ha iniciado
				if(ob==miVista.getTablero()[i][k]){
					if(!miModelo.getPulsado()[i][k] && !miModelo.getBanderas()[i][k]){// Si no se ha pulsado (para que no se pulse 2 veces) y si no hay una bandera (al poner una bandera no se puede pulsar)
						
						pulsar(i,k);
						
						// INICIAR - Mientras el crono no este activo y no halla una bomba
						if(!cronoActivo && !miModelo.getBombas()[i][k]){
							crono.start();
							cronoActivo = true;
						}
						
						// DETENER Perdiendo - si se pulsa una bomba (es necesario que se compruebe despues de pulsar(), ya que sino,
						// si hay bomba, pulsar() pulsa todas las bombas, y si ya estaban todas pulsadas no puede pulsar las bombas )
						if(miModelo.getBombas()[i][k]){
							crono.interrupt();//Para el crono
							cronoActivo = false;
							pulsarTodo();// lo pulso todo(miModelo.getPulsado()) para que no se pueda pulsar una casilla despues de pulsar una bomba
							
							// Mensaje al jugador
							JOptionPane.showMessageDialog(miVista, "Lo sentimos, p�rdio este juego.", "Perdi� el juego",
									JOptionPane.INFORMATION_MESSAGE);
							
							try {
								
								// A�adir la puntuacion
								if((alto==9 && ancho==9 && minas==10) || (alto==16 && ancho==16 && minas==40) || (alto==16 && ancho==30 && minas==99))// Si la partida es personalizada
									est.anadir(null, false);//false = perdida
								
							}
							catch (NumberFormatException e1) {e1.printStackTrace();}
							catch (IOException e2) {e2.printStackTrace();}
							
							// Iniciar nuevo juego
							nuevoJuego();

						}
						
						// DETENER Ganando - (es necesario que se compruebe despues del pulsar(), ya que sino, todavia no se habria pulsado la ultima casilla)
						if(miModelo.heGanado()){
							crono.interrupt();// Para el crono
							cronoActivo = false;

							try {
								if((alto==9 && ancho==9 && minas==10) || (alto==16 && ancho==16 && minas==40) || (alto==16 && ancho==30 && minas==99)){// Si la partida no es personalizada
									if(est.seGuarda(Integer.parseInt(miVista.getlTiempo().getText()))){// Pregunta si se guarda esa puntuacion segun las existentes
										g = new DialogoGanar(miVista.getlTiempo().getText());// llama al dialogo 
										est.anadir( new Puntuacion(Integer.valueOf(miVista.getlTiempo().getText()),g.getNombre()), true );
									}
									else{// Mensaje al jugador
										JOptionPane.showMessageDialog(miVista, "Gan� este juego, pero su puntuaci�n no es de las 10 mejores.", "Gan� el juego",
												JOptionPane.INFORMATION_MESSAGE);
									}
									
									
									
								}
								else{// si la partida es personalizada
									JOptionPane.showMessageDialog(miVista, "Gan� este juego.", "Gan� el juego",
											JOptionPane.INFORMATION_MESSAGE);
								}
								nuevoJuego();
								
							} 
							catch (NumberFormatException e2) {e2.printStackTrace();} 
							catch (IOException e2) {e2.printStackTrace();}
							
System.out.println("Has ganado!!!");
						}	
					}//End 2� if
				}//End 1� if
		
		// Barras
		if(ob==miVista.getMiNuevoJuego()){// Nuevo Juego
			nuevoJuego();
		}
		
		if(ob==miVista.getMiEstadisticas()){// Estadisticas
			try {
				
				new DialogoEstadisticas();
				
			}
			catch (ExcepcionPuntuaciones e1) {e1.printStackTrace();} 
			catch (IOException e1) {e1.printStackTrace();}
		}
				
		if(ob==miVista.getMiCambiarApariencia()){// Cambiar Apariencia
			
			ca = new DialogoCambiarApariencia();// dialogo Cambiar apariencia

			if(ca.getiBombaFinal() != null && ca.getiTableroFinal() != null){// si se ha pulsado Aceptar pongo los iconos seleccionados por el usuario, sino(Cancelar), los dejo como estaban anteriormente
				
				// INICIALIZAR ICONOS
				iBomba = ca.getiBombaFinal();// Paso al controlador la bomba seleccionada
				iTablero = ca.getiTableroFinal();// Paso al controlador el tablero seleccionado
				miVista.getlIconBombas().setIcon(iBomba);// Pongo el icono de la bomba seleccionada al label
				// Establezce el icono de la Bandera correspondiente
				if(iTablero.toString().equals("Buscaminas/img/tablero1_18.png"))// si es tablero1...
					iBandera = new ImageIcon("Buscaminas/img/tablero1_18_bandera.png");// ...inicializa el icono de la bandera a bandera1
				else// si es tablero2
					iBandera = new ImageIcon("Buscaminas/img/tablero2_18_bandera.png");// ...inicializa el icono de la bandera a bandera2

				
				
				// RECORRE LAS CASILLAS y les pone los iconos correspondientes
				for(int i = 0; i<alto; i++)
					for(int k = 0; k<ancho; k++){
						
						if(miVista.getTablero()[i][k].getIcon() != null){// Es necesario esta condicion por que si el Icono es nulo, al hacerle el toString, salta la excepcion NullPointerException
						
							// ICONO TABLERO
							if(miVista.getTablero()[i][k].getIcon().toString().equals("Buscaminas/img/tablero1_18.png") || miVista.getTablero()[i][k].getIcon().toString().equals("img/tablero2_18.png"))// si es tablero1 o tablero2...
								miVista.getTablero()[i][k].setIcon(iTablero);// ...asigna el tablero seleccionado por el jugador
							 
							// ICONO BOMBA
							if(miVista.getTablero()[i][k].getIcon().toString().equals("Buscaminas/img/bomba1_16.png") || miVista.getTablero()[i][k].getIcon().toString().equals("img/bomba2_16.png"))// si es bomba1 o bomba2...
								miVista.getTablero()[i][k].setIcon(iBomba);//...asigna la bomba seleccionada por el jugador
							
							// ICONO BANDERA
							if(miModelo.getBanderas()[i][k])// si en esa posicion hay una bandera...
								miVista.getTablero()[i][k].setIcon(iBandera);// ...asigna la bandera correspondiente, asignada a iBandera anteriormente 
						
						}
					}
			
			}//End if Aceptado.
		}
		
	
		if(ob==miVista.getMiSalir()){// Salir
			System.exit(0);
		}
		
		if(ob==miVista.getMiChivato()){// Chivato
			new DialogoChivato(miModelo.getBombas(), iBomba);
		}
			
			
	}//End ActionPerformed
	
	/**
	 * M�todo de la interfaz MouseListener
	 */
	public void mouseClicked(java.awt.event.MouseEvent e) {
		int bombas = Integer.valueOf(miVista.getlBombas().getText());
		
		if (e.getButton()==3){// 1=1�Boton del raton, 3=2�Boton del raton
			for(int i=0; i<alto; i++)
				for(int k=0; k<ancho; k++)
					if(miVista.getTablero()[i][k] == e.getSource()){// Si es el boton pulsado
						
						if(miVista.getTablero()[i][k].getIcon()==iBandera && !miModelo.getPulsado()[i][k]){// Si ya tenia bandera, y no esta pulsado...
							miModelo.getBanderas()[i][k]=false;//... indico que se ha quitado una bandera
							miVista.getTablero()[i][k].setIcon(iTablero);//... la quito
							miVista.getlBombas().setText(String.valueOf(bombas+1));//... incremento las bombas
						}
						else if(miVista.getTablero()[i][k].getIcon()!=iBandera && !miModelo.getPulsado()[i][k]){// Si no tenia bandera, y no esta pulsado...
							miModelo.getBanderas()[i][k]=true;//... indico que se ha puesto una bandera
							miVista.getTablero()[i][k].setIcon(iBandera);//... la pongo
							miVista.getlBombas().setText(String.valueOf(bombas-1));//... decremento las bombas
						}
						
					}
		}// 1� if
	}//End mouseClicked
	
	public void mouseEntered(java.awt.event.MouseEvent e) {}
	public void mouseExited(java.awt.event.MouseEvent e) {}
	public void mousePressed(java.awt.event.MouseEvent e) {}
	public void mouseReleased(java.awt.event.MouseEvent e) {}
	
	/*
	 * M�TODOS PARA EL FUNCIONAMIENTO
	 */
	/**
	 * Pulsar Alrededor (pulsa las casillas de alrededor de una dada)
	 * @param i
	 * @param k
	 */
	private void pulsarAlrededor(int i, int k){

			if(i == 0 && k == 0){// casilla superior izquierda
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);
				if(!miModelo.getPulsado()[i+1][k+1])pulsar(i+1, k+1);
			}
			if(i == 0 && k == ancho-1){// casilla superior derecha
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);				
				if(!miModelo.getPulsado()[i+1][k-1])pulsar(i+1, k-1);				
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);
			}
			if(i == alto-1 && k == 0){// casilla inferior izquierda
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i-1][k+1])pulsar(i-1, k+1);				
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);
			}
			if(i == alto-1 && k == ancho-1){// casilla inferior derecha
				if(!miModelo.getPulsado()[i-1][k-1])pulsar(i-1, k-1);				
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);
			}
			
			
			if(i == 0 && k != 0 && k != ancho-1){// arriba (menos las 2 esquinas)
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);				
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);				
				if(!miModelo.getPulsado()[i+1][k-1])pulsar(i+1, k-1);				
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);				
				if(!miModelo.getPulsado()[i+1][k+1])pulsar(i+1, k+1);
			}		
			if(i == alto-1 && k != 0 && k != ancho-1){// abajo (menos las 2 esquinas)
				if(!miModelo.getPulsado()[i-1][k-1])pulsar(i-1, k-1);				
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i-1][k+1])pulsar(i-1, k+1);				
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);				
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);
			}
			if(k == 0 && i != 0 && i != alto-1){// iquierda (menos las 2 esquinas)
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i-1][k+1])pulsar(i-1, k+1);				
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);				
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);				
				if(!miModelo.getPulsado()[i+1][k+1])pulsar(i+1, k+1);
			}
			if(k == ancho-1 && i != 0 && i != alto-1){// derecha (menos las 2 esquinas)
				if(!miModelo.getPulsado()[i-1][k-1])pulsar(i-1, k-1);				
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);				
				if(!miModelo.getPulsado()[i+1][k-1])pulsar(i+1, k-1);				
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);
			}
			
			if(k != 0 && k != ancho-1 && i != 0 && i != alto-1){// casillas del centro (menos las de los bordes)
				if(!miModelo.getPulsado()[i-1][k-1])pulsar(i-1, k-1);				
				if(!miModelo.getPulsado()[i-1][k])pulsar(i-1, k);				
				if(!miModelo.getPulsado()[i-1][k+1])pulsar(i-1, k+1);				
				if(!miModelo.getPulsado()[i][k-1])pulsar(i, k-1);				
				if(!miModelo.getPulsado()[i][k+1])pulsar(i, k+1);				
				if(!miModelo.getPulsado()[i+1][k-1])pulsar(i+1, k-1);				
				if(!miModelo.getPulsado()[i+1][k])pulsar(i+1, k);
				if(!miModelo.getPulsado()[i+1][k+1])pulsar(i+1, k+1);
				
			}
	}
	
	/**
	 * Pulsar (M�todo que pulsa una casilla concreta y le aplica una serie de propiedades) 
	 */
	private void pulsar(int i, int k){
		
		// si no hay bandera, lo pulsamos(con bandera no podemos pulsar), lo controlo aqui tambien aparte de en el ActionPerformed por que si la casilla que escucha el 
		// ActionPerformed no es bandera, no tiene bombas lindantes, y tiene banderas lindantes, estas si que se pulsarian al pulsar alrededor de una casilla sin bombas lindantes
		if(!miModelo.getBanderas()[i][k]){
		
			numCasilla = miModelo.pulsar(i, k);// n� de la casilla (bombas lindantes)
			miVista.getTablero()[i][k].setIcon(null);// le quito el icono de fondo
			
			// pone el n� de bombas lindantes en la casilla (si es 0 no ponemos texto (0 = ""), si es 999 bombaPulsada(), que devuelve "", y en otro caso pone el n� de bombas)
			miVista.getTablero()[i][k].setText( String.valueOf(   (numCasilla == 0 ? "" : (numCasilla == 999 ? (bombaPulsada()) :numCasilla))   ));
			miVista.getTablero()[i][k].setBackground(Color.WHITE);// Establecer color fondo
			miVista.getTablero()[i][k].setFont(fCasillas);//Establecer fuente
			
			if(numCasilla != 0 && numCasilla != 999)miVista.getTablero()[i][k].setForeground(colorNumero(numCasilla));// Asignar color a la casilla(si es 0 o 999"Bomba", no se asigna color)
			
			if(numCasilla == 0){// Si es 0, pulsa las de alrededor
				pulsarAlrededor(i, k);// pulsa las casillas de alrededor de (i,k)
				miVista.getTablero()[i][k].setText("");// no tiene texto de fondo
			}
		}
	}

	
	/**
	 * Bomba pulsada (cuando se pulsa una bomba se pulsan y se pulsan todas)
	 * @return devuelve un String vacio para adaptarlo al condicional ?: del m�todo pulsar(i,k)
	 */
	private String bombaPulsada(){
		
			for (int i=0; i<alto; i++)
				for (int k=0; k<ancho; k++){
					if(miModelo.getBombas()[i][k]){
						miVista.getTablero()[i][k].setIcon(iBomba);//si no se ha seleccionado ningun Icono bomba, poner el de por defecto, sino, poner el seleccionado
						miVista.getTablero()[i][k].setBackground(Color.WHITE);// fondo blanco
						miModelo.getPulsado()[i][k] = true;// casilla pulsada
					}
				}
			
		return "";
	}
	

	/**
	 * Color del Numero de la casilla (segun el numero de bombas lindantes, el n� de la casilla tiene un color u otro)
	 * @param n (n� de bombas lindantes)
	 * @return (Color correspondiente)
	 */
	private Color colorNumero(int n){
		Color c;
		switch (n) {
		case 1:c = Color.CYAN;break;
		case 2:c = Color.GREEN;break;
		case 3:c = Color.RED;break;
		case 4:c = Color.BLUE;break;
		case 5:c = new Color(136,0,21);break;//Marron
		case 6:c = new Color(43,136,126);break;//Verde 2
		case 7:c = Color.BLACK;break;
		case 8:c = Color.ORANGE;break;
		default:c = null;break;
		}
		
		return c;
	}
		
	/**
	 * Pulsar todo (pulsa todas las casillas del Modelo, este m�todo se utiliza cuando se pierda la partida(bomba pulsada), 
	 * se ponen todas las casillas a pulsadas y no se pueden pulsar mas casillas hasta que se inicie un nuevo juego)
	 */
	private void pulsarTodo(){
		for(int i=0; i<alto; i++)
			for(int k=0; k<ancho; k++){
				miModelo.getPulsado()[i][k]=true;
			}
	}

	/**
	 * Nuevo juego (reinicia lo necesario para iniciar un nuevo juego de la misma Dificultad que la actual)
	 */
	public void nuevoJuego(){
		//miModelo = null;
		miModelo = new Modelo(alto, ancho, minas);// Nuevo juego con las bombas en diferentes sitios(el anterior se queda sin referencia y lo elimina el recolector automaticamente)
		miVista.getlBombas().setText(String.valueOf(minas));// Actualiza el label con las bombas totales(por si se habia puesto una bandera y se ha decrementado el n� de bombas)
		
		// Poner los botones de la Vista como aparecen inicialmente
		for(int i=0; i<alto; i++)
			for(int k=0; k<ancho; k++){
				miVista.getTablero()[i][k].setIcon(iTablero);// Le pongo el icono del tablero
				miVista.getTablero()[i][k].setText("");// Texto vacio
			}
		
		reiniciarCrono();// Reinicia el cronometro
	}
	
	/*
	 * CRONOMETRO 
	 */
	/**
	 * Cronometro (M�todo de la interfaz Runnable)
	 */
	@SuppressWarnings("static-access")
	public void run() {
		try {
			while(segundos < 999) {// El m�ximo de tiempo sera 999, a los 999 se para (como en el original de windows)
				segundos++;// incrementa en 1 los segundos
				
				miVista.getlTiempo().setText(String.valueOf(segundos));// por cada ciclo del bucle asigna a un Label el tiempo actual.
				
				crono.sleep(1000);//espera 1 segundo
			}
		}
		catch (InterruptedException e) {
			System.out.println(e.getMessage()); 
		}
		
	}

	/**
	 * ReiniciarCrono (reinicia el cronometro a 0 y lo para, esperando a que lo inicie el jugador al pulsar una casilla en un nuevo juego)
	 */
	private void reiniciarCrono(){
		if(cronoActivo){// si el cronometro esta activo...
			crono.interrupt();// ... lo paro
			cronoActivo = false;// ... y indico que lo he parado
		}
		segundos = 0;// variable que utiliza el cronometro para incrementar los segundos
		miVista.getlTiempo().setText("0");// variable donde le mostramos al jugador los segundos
		crono = new Thread(this);// nuevo objeto del cronometro
	}
	



}
